package com.company.common.exception;

import com.alibaba.fastjson.JSON;
import com.company.common.enums.CommonErrorEnum;
import com.company.common.enums.ExpCodeEnum;
import com.company.common.enums.ProjectConstants;
import com.company.user.vo.GeneralResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.MDC;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

/**
 * 统一异常处理
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LogManager.getLogger("ProjectLogAspect");

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public GeneralResult<?> errorHandler(Exception ex) {
        String resultCode;
        StringBuilder errMsg = new StringBuilder();

        if (ex instanceof HttpRequestMethodNotSupportedException) {
            resultCode = "" + ExpCodeEnum.MISSING_SUPPORTED_METHODS.getCode();
            errMsg = new StringBuilder("不支持当前请求方式！请使用 " + Arrays.toString(((HttpRequestMethodNotSupportedException) ex).getSupportedMethods()) + " 方式请求");
        }
        else if (ex instanceof MissingServletRequestParameterException) {
            resultCode = "" + ExpCodeEnum.MISSING_REQUIRED_ARGUMENTS.getCode();
            errMsg = new StringBuilder("缺少请求参数！ [" + ((MissingServletRequestParameterException) ex).getParameterType() + "]" + ((MissingServletRequestParameterException) ex).getParameterName());
        }
        else if (ex instanceof MethodArgumentNotValidException) {
            resultCode = "" + ExpCodeEnum.INVALID_ARGUMENTS.getCode();
            BindingResult bindingResult = ((MethodArgumentNotValidException) ex).getBindingResult();
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            for (ObjectError error : allErrors) {
                errMsg.append(error.getDefaultMessage()).append("\n");
            }
        }
        else if (ex instanceof HttpMessageNotReadableException) {
            resultCode = "" + ExpCodeEnum.REQUEST_BODY_IS_MISSING.getCode();
            errMsg = new StringBuilder("未从请求体中找到接口必传参数");
        }
        else if (ex instanceof BizException) {
            BizException tmp = (BizException) ex;
            resultCode = tmp.getErrorCode();
            errMsg = new StringBuilder(tmp.getErrorMsg());
        }
        else if (ex instanceof PersistenceException) {
            // 断言抛出的异常，一般是函数入参有问题 抛出异常的地方必须在函数调用前进行参数校验
            resultCode = "500";
            errMsg = new StringBuilder("数据库异常,请联系系统维护人员");
        }
        else if (ex instanceof IllegalArgumentException) {
            // 断言抛出的异常，一般是函数入参有问题 抛出异常的地方必须在函数调用前进行参数校验
            resultCode = "500";
            errMsg = new StringBuilder("系统错误,请联系开发人员");
        }
        else {
            resultCode = "" + ExpCodeEnum.UNKNOWN_ERROR.getCode();
            errMsg = new StringBuilder("未知错误," + ex.toString());
        }

        // 记录异常到日志页面
        printProjectException("全局捕获到异常", ex);

        GeneralResult<?> respData = GeneralResult.genErrorResult(errMsg.toString(), resultCode, MDC.get("traceId"));


        printRsp(respData);

        return respData;

    }

    public static void printProjectException(String str, Exception e) {
        StringBuilder sb = new StringBuilder();
        sb.append(str).append("\n");
        sb.append(e.toString()).append("\n");
        StackTraceElement[] trace = e.getStackTrace();
        List<StackTraceElement> list = Arrays.asList(trace);
        for (int i = 0; i < list.size(); i++) {
            StackTraceElement el = list.get(i);
            String elString = el.toString();
            if (elString.startsWith("com.company.") && !elString.endsWith("(<generated>)")) {
                sb.append(String.format("    [TRACE %03d]", i)).append(" ").append(el.toString()).append("\n");
            }
        }
        logger.error(sb.toString());
    }

    public static String replaceAllImgBase64(String json) {
        if (json.length() > 300 * 20) {
            json = json.replaceAll("<img src=.?\\\"data:image/.+?;base64,.+?\\\">", "<img base64 json ignore>");
        }
        return json;
    }

    public static void printRsp(Object rsp) {
        if (rsp == null) {
            logger.info("rsp:null");
        }
        else if (rsp instanceof Void) {
        }
        else {
            String rspJson = JSON.toJSONString(rsp);
            rspJson = GlobalExceptionHandler.replaceAllImgBase64(rspJson);
            logger.info("rsp:" + rspJson);
        }
    }
}
